﻿using Bettzueche.RLLibrary.Functions;
using Bettzueche.RLLibrary.Types;
using System;
using System.Collections.ObjectModel;

namespace RLProject1.Model {
    /// <summary>
    /// Kollektion von Zuständen mit ihren jeweiligen Q-Werten (<see cref="GridStateQValues"/>). Implementiert die Schätzfunktion
    /// IQValueFunction&lt;GridState, GridAction&gt;: f(state,action) = q.
    /// <para>Diese Observable Collection kann dazu dienen, die gesamten Q-Werte der Welt zu halten.</para>
    /// </summary>
    public class GridWorldQValues : ObservableCollection<GridStateQValues>, IStateValueFunction<GridState, GridAction> {

        //double[,] qVals;

        /// <summary>
        /// Erzeugt und initialisiert eine neue Kollektion mit der angegebenen Anzahl von GridStateQValues.
        /// </summary>
        /// <see cref="GridStateQValues"/>
        /// <param name="numOfStates"></param>
        public GridWorldQValues(int numOfStates) : base() {
            //qVals = new double[numOfStates, Enum.GetValues(typeof(GridActionTypes)).Length];
            for (int i = 0; i < numOfStates; i++) {
                GridStateQValues item = new GridStateQValues(new GridState { ID = i });
                base.Add(item);
            }
        }

        #region IStateValueFunction<GridState,GridAction> Members

        /// <summary>
        /// Gibt oder setzt den Schätzwert Q zum gegebenen Zustands-Aktions-Paar.
        /// </summary>
        /// <param name="state">Zustand (Weltposition)</param>
        /// <param name="action">Aktion</param>
        /// <returns>Schätzwert Q zum gegebenen Zustands-Aktions-Paar.</returns>
        public double this[GridState state, GridAction action] {
            get {
                //return qVals[state.ID, (int)action.Direction];
                return base[state.ID][action];
            }
            set {
                //qVals[state.ID, (int)action.Direction] = value;
                base[state.ID][action] = value;
            }
        }

        /// <summary>
        /// Gets or sets the <see cref="GridStateQValues"/> with the specified state ID.
        /// </summary>
        /// <value> The <see cref="GridStateQValues"/>. </value>
        /// <param name="stateID">The state ID.</param>
        /// <returns>The <see cref="GridStateQValues"/> for the specified state ID.</returns>
        /// <exception cref="System.ArArgumentOutOfRangeException">stateID is less than zero. -or- stateID is equal to or greater than Count.</exception>
        public new GridStateQValues this[int stateID] {
            get { return base[stateID]; }
            set { base[stateID] = value; }
        }

        /// <summary>
        /// Gibt die Anzahl der Zustände, bzw. der <see cref="GridStateQValues"/> in dieser Kollektion / IStateValueFunction.
        /// </summary>
        /// <value>
        /// Anzahl der Zustände.
        /// </value>
        public new int Count {
            get { return base.Count; }
        }

        /// <summary>
        /// Gibt die Q-Werte zu allen im spezifizierten Zustand ausführbaren Aktion.
        /// </summary>
        /// <param name="state">The state.</param>
        /// <returns>Alle q-Werte zum angegebenen Zustand</returns>
        public double[] GetEstimates(GridState state)
        {
            return base[state.ID].All;
        }

        public void Add(IEligibilityFunction<GridAction, GridState> eFunction, double factor) {
            foreach (var sa in eFunction.Path) {
                base[sa.State.ID][sa.Action] += eFunction[sa.Action, sa.State] * factor;
            }
            //foreach (GridStateQValues q_s in this) {
            //    GridState s = q_s.State;
            //    q_s.O = eFunction[GridAction.EAST, s] * factor;
            //    q_s.NO = eFunction[GridAction.NORTHEAST, s] * factor;
            //    q_s.N = eFunction[GridAction.NORTH, s] * factor;
            //    q_s.NW = eFunction[GridAction.NORTHWEST, s] * factor;
            //    q_s.W = eFunction[GridAction.WEST, s] * factor;
            //    q_s.SW = eFunction[GridAction.SOUTHWEST, s] * factor;
            //    q_s.S = eFunction[GridAction.SOUTH, s] * factor;
            //    q_s.SO = eFunction[GridAction.SOUTHEAST, s] * factor;
            //}
        }

        public void Add(IEligibilityFunction<GridAction, GridState> eFunction) {
            this.Add(eFunction, 1.0);
        }

        /// <summary>
        /// Not Implemented!!!
        /// </summary>
        /// <param name="m">The m.</param>
        /// <exception cref="System.NotImplementedException"></exception>
        public void Add(Matrix m) {
            throw new NotImplementedException();
        }

        #endregion


        #region IStateValueFunction Members

        public double this[IState state, IAction action] {
            get {
                if (state is GridState) {
                    if (action is GridAction) {
                        return this[(GridState)state, (GridAction)action];
                    }
                }
                throw new NotSupportedException();
            }
            set {
                if (state is GridState) {
                    if (action is GridAction) {
                        this[(GridState)state, (GridAction)action] = value;
                    }
                }
                throw new NotSupportedException();
            }
        }

        public double[] GetEstimates(IState state) {
            if (state is GridState) {
                return this.GetEstimates((GridState)state);
            }
            throw new NotSupportedException("state Impl. nicht unterstützt");
        }

        #endregion

    }

}
